1 module hip.audio_decoding.resampler; 2 3 version(none): //Currently unused as audio players already resample. 4 5 /* Copyright (C) 2007-2008 Jean-Marc Valin 6 * Copyright (C) 2008 Thorvald Natvig 7 * D port by Ketmar // Invisible Vector 8 * D Source took from ARSD codebase 9 * 10 * Arbitrary resampling code 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions are 14 * met: 15 * 16 * 1. Redistributions of source code must retain the above copyright notice, 17 * this list of conditions and the following disclaimer. 18 * 19 * 2. Redistributions in binary form must reproduce the above copyright 20 * notice, this list of conditions and the following disclaimer in the 21 * documentation and/or other materials provided with the distribution. 22 * 23 * 3. The name of the author may not be used to endorse or promote products 24 * derived from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 27 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 28 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 29 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 30 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 31 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 32 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 34 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 35 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 /* A-a-a-and now... D port is covered by the following license! 40 * 41 * This program is free software: you can redistribute it and/or modify 42 * it under the terms of the GNU General Public License as published by 43 * the Free Software Foundation, either version 3 of the License, or 44 * (at your option) any later version. 45 * 46 * This program is distributed in the hope that it will be useful, 47 * but WITHOUT ANY WARRANTY; without even the implied warranty of 48 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 49 * GNU General Public License for more details. 50 * 51 * You should have received a copy of the GNU General Public License 52 * along with this program. If not, see <http://www.gnu.org/licenses/>. 53 */ 54 //module iv.follin.resampler /*is aliced*/; 55 //import iv.alice; 56 57 /* 58 The design goals of this code are: 59 - Very fast algorithm 60 - SIMD-friendly algorithm 61 - Low memory requirement 62 - Good *perceptual* quality (and not best SNR) 63 Warning: This resampler is relatively new. Although I think I got rid of 64 all the major bugs and I don't expect the API to change anymore, there 65 may be something I've missed. So use with caution. 66 This algorithm is based on this original resampling algorithm: 67 Smith, Julius O. Digital Audio Resampling Home Page 68 Center for Computer Research in Music and Acoustics (CCRMA), 69 Stanford University, 2007. 70 Web published at http://www-ccrma.stanford.edu/~jos/resample/. 71 There is one main difference, though. This resampler uses cubic 72 interpolation instead of linear interpolation in the above paper. This 73 makes the table much smaller and makes it possible to compute that table 74 on a per-stream basis. In turn, being able to tweak the table for each 75 stream makes it possible to both reduce complexity on simple ratios 76 (e.g. 2/3), and get rid of the rounding operations in the inner loop. 77 The latter both reduces CPU time and makes the algorithm more SIMD-friendly. 78 */ 79 version = sincresample_use_full_table; 80 version(X86) { 81 version(sincresample_disable_sse) { 82 } else { 83 version(D_PIC) {} else version = sincresample_use_sse; 84 } 85 } 86 87 88 // ////////////////////////////////////////////////////////////////////////// // 89 public struct SpeexResampler { 90 public: 91 alias Quality = int; 92 enum : uint { 93 Fastest = 0, 94 Voip = 3, 95 Default = 4, 96 Desktop = 5, 97 Music = 8, 98 Best = 10, 99 } 100 101 enum Error { 102 OK = 0, 103 NoMemory, 104 BadState, 105 BadArgument, 106 BadData, 107 } 108 109 private: 110 nothrow @trusted @nogc: 111 alias ResamplerFn = int function (ref SpeexResampler st, uint chanIdx, const(float)* indata, uint *indataLen, float *outdata, uint *outdataLen); 112 113 private: 114 uint inRate; 115 uint outRate; 116 uint numRate; // from 117 uint denRate; // to 118 119 Quality srQuality; 120 uint chanCount; 121 uint filterLen; 122 uint memAllocSize; 123 uint bufferSize; 124 int intAdvance; 125 int fracAdvance; 126 float cutoff; 127 uint oversample; 128 bool started; 129 130 // these are per-channel 131 int[64] lastSample; 132 uint[64] sampFracNum; 133 uint[64] magicSamples; 134 135 float* mem; 136 uint realMemLen; // how much memory really allocated 137 float* sincTable; 138 uint sincTableLen; 139 uint realSincTableLen; // how much memory really allocated 140 ResamplerFn resampler; 141 142 int inStride; 143 int outStride; 144 145 public: 146 static string errorStr (int err) { 147 switch (err) with (Error) { 148 case OK: return "success"; 149 case NoMemory: return "memory allocation failed"; 150 case BadState: return "bad resampler state"; 151 case BadArgument: return "invalid argument"; 152 case BadData: return "bad data passed"; 153 default: 154 } 155 return "unknown error"; 156 } 157 158 public: 159 @disable this (this); 160 ~this () { deinit(); } 161 162 @property bool inited () const pure { return (resampler !is null); } 163 164 void deinit () { 165 import core.stdc.stdlib : free; 166 if (mem !is null) { free(mem); mem = null; } 167 if (sincTable !is null) { free(sincTable); sincTable = null; } 168 /* 169 memAllocSize = realMemLen = 0; 170 sincTableLen = realSincTableLen = 0; 171 resampler = null; 172 started = false; 173 */ 174 inRate = outRate = numRate = denRate = 0; 175 srQuality = cast(Quality)666; 176 chanCount = 0; 177 filterLen = 0; 178 memAllocSize = 0; 179 bufferSize = 0; 180 intAdvance = 0; 181 fracAdvance = 0; 182 cutoff = 0; 183 oversample = 0; 184 started = 0; 185 186 mem = null; 187 realMemLen = 0; // how much memory really allocated 188 sincTable = null; 189 sincTableLen = 0; 190 realSincTableLen = 0; // how much memory really allocated 191 resampler = null; 192 193 inStride = outStride = 0; 194 } 195 196 /** Create a new resampler with integer input and output rates. 197 * 198 * Params: 199 * chans = Number of channels to be processed 200 * inRate = Input sampling rate (integer number of Hz). 201 * outRate = Output sampling rate (integer number of Hz). 202 * aquality = Resampling quality between 0 and 10, where 0 has poor quality and 10 has very high quality. 203 * 204 * Returns: 205 * 0 or error code 206 */ 207 Error setup (uint chans, uint ainRate, uint aoutRate, Quality aquality/*, usize line=__LINE__*/) { 208 //{ import core.stdc.stdio; printf("init: %u -> %u at %u\n", ainRate, aoutRate, cast(uint)line); } 209 import core.stdc.stdlib : malloc, free; 210 211 deinit(); 212 if (aquality < 0) aquality = 0; 213 if (aquality > SpeexResampler.Best) aquality = SpeexResampler.Best; 214 if (chans < 1 || chans > 16) return Error.BadArgument; 215 216 started = false; 217 inRate = 0; 218 outRate = 0; 219 numRate = 0; 220 denRate = 0; 221 srQuality = cast(Quality)666; // it's ok 222 sincTableLen = 0; 223 memAllocSize = 0; 224 filterLen = 0; 225 mem = null; 226 resampler = null; 227 228 cutoff = 1.0f; 229 chanCount = chans; 230 inStride = 1; 231 outStride = 1; 232 233 bufferSize = 160; 234 235 // per channel data 236 lastSample[] = 0; 237 magicSamples[] = 0; 238 sampFracNum[] = 0; 239 240 setQuality(aquality); 241 setRate(ainRate, aoutRate); 242 243 if (auto filterErr = updateFilter()) { deinit(); return filterErr; } 244 skipZeros(); // make sure that the first samples to go out of the resamplers don't have leading zeros 245 246 return Error.OK; 247 } 248 249 /** Set (change) the input/output sampling rates (integer value). 250 * 251 * Params: 252 * ainRate = Input sampling rate (integer number of Hz). 253 * aoutRate = Output sampling rate (integer number of Hz). 254 * 255 * Returns: 256 * 0 or error code 257 */ 258 Error setRate (uint ainRate, uint aoutRate/*, usize line=__LINE__*/) { 259 //{ import core.stdc.stdio; printf("changing rate: %u -> %u at %u\n", ainRate, aoutRate, cast(uint)line); } 260 if (inRate == ainRate && outRate == aoutRate) return Error.OK; 261 //{ import core.stdc.stdio; printf("changing rate: %u -> %u at %u\n", ratioNum, ratioDen, cast(uint)line); } 262 263 uint oldDen = denRate; 264 inRate = ainRate; 265 outRate = aoutRate; 266 auto div = gcd(ainRate, aoutRate); 267 numRate = ainRate/div; 268 denRate = aoutRate/div; 269 270 if (oldDen > 0) { 271 foreach (ref v; sampFracNum.ptr[0..chanCount]) { 272 v = v*denRate/oldDen; 273 // safety net 274 if (v >= denRate) v = denRate-1; 275 } 276 } 277 278 return (inited ? updateFilter() : Error.OK); 279 } 280 281 /** Get the current input/output sampling rates (integer value). 282 * 283 * Params: 284 * ainRate = Input sampling rate (integer number of Hz) copied. 285 * aoutRate = Output sampling rate (integer number of Hz) copied. 286 */ 287 void getRate (out uint ainRate, out uint aoutRate) { 288 ainRate = inRate; 289 aoutRate = outRate; 290 } 291 292 @property uint getInRate () { return inRate; } 293 @property uint getOutRate () { return outRate; } 294 295 @property uint getChans () { return chanCount; } 296 297 /** Get the current resampling ratio. This will be reduced to the least common denominator. 298 * 299 * Params: 300 * ratioNum = Numerator of the sampling rate ratio copied 301 * ratioDen = Denominator of the sampling rate ratio copied 302 */ 303 void getRatio (out uint ratioNum, out uint ratioDen) { 304 ratioNum = numRate; 305 ratioDen = denRate; 306 } 307 308 /** Set (change) the conversion quality. 309 * 310 * Params: 311 * quality = Resampling quality between 0 and 10, where 0 has poor quality and 10 has very high quality. 312 * 313 * Returns: 314 * 0 or error code 315 */ 316 Error setQuality (Quality aquality) { 317 if (aquality < 0) aquality = 0; 318 if (aquality > SpeexResampler.Best) aquality = SpeexResampler.Best; 319 if (srQuality == aquality) return Error.OK; 320 srQuality = aquality; 321 return (inited ? updateFilter() : Error.OK); 322 } 323 324 /** Get the conversion quality. 325 * 326 * Returns: 327 * Resampling quality between 0 and 10, where 0 has poor quality and 10 has very high quality. 328 */ 329 int getQuality () { return srQuality; } 330 331 /** Get the latency introduced by the resampler measured in input samples. 332 * 333 * Returns: 334 * Input latency; 335 */ 336 int inputLatency () { return filterLen/2; } 337 338 /** Get the latency introduced by the resampler measured in output samples. 339 * 340 * Returns: 341 * Output latency. 342 */ 343 int outputLatency () { return ((filterLen/2)*denRate+(numRate>>1))/numRate; } 344 345 /* Make sure that the first samples to go out of the resamplers don't have 346 * leading zeros. This is only useful before starting to use a newly created 347 * resampler. It is recommended to use that when resampling an audio file, as 348 * it will generate a file with the same length. For real-time processing, 349 * it is probably easier not to use this call (so that the output duration 350 * is the same for the first frame). 351 * 352 * Setup/reset sequence will automatically call this, so it is private. 353 */ 354 private void skipZeros () { foreach (immutable i; 0..chanCount) lastSample.ptr[i] = filterLen/2; } 355 356 static struct Data { 357 const(float)[] dataIn; 358 float[] dataOut; 359 uint inputSamplesUsed; // out value, in samples (i.e. multiplied by channel count) 360 uint outputSamplesUsed; // out value, in samples (i.e. multiplied by channel count) 361 } 362 363 /** Resample (an interleaved) float array. The input and output buffers must *not* overlap. 364 * `data.dataIn` can be empty, but `data.dataOut` can't. 365 * Function will return number of consumed samples (*not* *frames*!) in `data.inputSamplesUsed`, 366 * and number of produced samples in `data.outputSamplesUsed`. 367 * You should provide enough samples for all channels, and all channels will be processed. 368 * 369 * Params: 370 * data = input and output buffers, number of frames consumed and produced 371 * 372 * Returns: 373 * 0 or error code 374 */ 375 Error process(string mode="interleaved") (ref Data data) { 376 static assert(mode == "interleaved" || mode == "sequential"); 377 378 data.inputSamplesUsed = data.outputSamplesUsed = 0; 379 if (!inited) return Error.BadState; 380 381 if (data.dataIn.length%chanCount || data.dataOut.length < 1 || data.dataOut.length%chanCount) return Error.BadData; 382 if (data.dataIn.length > uint.max/4 || data.dataOut.length > uint.max/4) return Error.BadData; 383 384 static if (mode == "interleaved") { 385 inStride = outStride = chanCount; 386 } else { 387 inStride = outStride = 1; 388 } 389 uint iofs = 0, oofs = 0; 390 immutable uint idclen = cast(uint)(data.dataIn.length/chanCount); 391 immutable uint odclen = cast(uint)(data.dataOut.length/chanCount); 392 foreach (immutable i; 0..chanCount) { 393 data.inputSamplesUsed = idclen; 394 data.outputSamplesUsed = odclen; 395 if (data.dataIn.length) { 396 processOneChannel(i, data.dataIn.ptr+iofs, &data.inputSamplesUsed, data.dataOut.ptr+oofs, &data.outputSamplesUsed); 397 } else { 398 processOneChannel(i, null, &data.inputSamplesUsed, data.dataOut.ptr+oofs, &data.outputSamplesUsed); 399 } 400 static if (mode == "interleaved") { 401 ++iofs; 402 ++oofs; 403 } else { 404 iofs += idclen; 405 oofs += odclen; 406 } 407 } 408 data.inputSamplesUsed *= chanCount; 409 data.outputSamplesUsed *= chanCount; 410 return Error.OK; 411 } 412 413 414 //HACK for libswresample 415 // return -1 or number of outframes 416 int swrconvert (float** outbuf, int outframes, const(float)**inbuf, int inframes) { 417 if (!inited || outframes < 1 || inframes < 0) return -1; 418 inStride = outStride = 1; 419 Data data; 420 foreach (immutable i; 0..chanCount) { 421 data.dataIn = (inframes ? inbuf[i][0..inframes] : null); 422 data.dataOut = (outframes ? outbuf[i][0..outframes] : null); 423 data.inputSamplesUsed = inframes; 424 data.outputSamplesUsed = outframes; 425 if (inframes > 0) { 426 processOneChannel(i, data.dataIn.ptr, &data.inputSamplesUsed, data.dataOut.ptr, &data.outputSamplesUsed); 427 } else { 428 processOneChannel(i, null, &data.inputSamplesUsed, data.dataOut.ptr, &data.outputSamplesUsed); 429 } 430 } 431 return data.outputSamplesUsed; 432 } 433 434 /// Reset a resampler so a new (unrelated) stream can be processed. 435 void reset () { 436 lastSample[] = 0; 437 magicSamples[] = 0; 438 sampFracNum[] = 0; 439 //foreach (immutable i; 0..chanCount*(filterLen-1)) mem[i] = 0; 440 if (mem !is null) mem[0..chanCount*(filterLen-1)] = 0; 441 skipZeros(); // make sure that the first samples to go out of the resamplers don't have leading zeros 442 } 443 444 private: 445 Error processOneChannel (uint chanIdx, const(float)* indata, uint* indataLen, float* outdata, uint* outdataLen) { 446 uint ilen = *indataLen; 447 uint olen = *outdataLen; 448 float* x = mem+chanIdx*memAllocSize; 449 immutable int filterOfs = filterLen-1; 450 immutable uint xlen = memAllocSize-filterOfs; 451 immutable int istride = inStride; 452 if (magicSamples.ptr[chanIdx]) olen -= magic(chanIdx, &outdata, olen); 453 if (!magicSamples.ptr[chanIdx]) { 454 while (ilen && olen) { 455 uint ichunk = (ilen > xlen ? xlen : ilen); 456 uint ochunk = olen; 457 if (indata !is null) { 458 //foreach (immutable j; 0..ichunk) x[j+filterOfs] = indata[j*istride]; 459 if (istride == 1) { 460 x[filterOfs..filterOfs+ichunk] = indata[0..ichunk]; 461 } else { 462 auto sp = indata; 463 auto dp = x+filterOfs; 464 foreach (immutable j; 0..ichunk) { *dp++ = *sp; sp += istride; } 465 } 466 } else { 467 //foreach (immutable j; 0..ichunk) x[j+filterOfs] = 0; 468 x[filterOfs..filterOfs+ichunk] = 0; 469 } 470 processNative(chanIdx, &ichunk, outdata, &ochunk); 471 ilen -= ichunk; 472 olen -= ochunk; 473 outdata += ochunk*outStride; 474 if (indata !is null) indata += ichunk*istride; 475 } 476 } 477 *indataLen -= ilen; 478 *outdataLen -= olen; 479 return Error.OK; 480 } 481 482 Error processNative (uint chanIdx, uint* indataLen, float* outdata, uint* outdataLen) { 483 immutable N = filterLen; 484 int outSample = 0; 485 float* x = mem+chanIdx*memAllocSize; 486 uint ilen; 487 started = true; 488 // call the right resampler through the function ptr 489 outSample = resampler(this, chanIdx, x, indataLen, outdata, outdataLen); 490 if (lastSample.ptr[chanIdx] < cast(int)*indataLen) *indataLen = lastSample.ptr[chanIdx]; 491 *outdataLen = outSample; 492 lastSample.ptr[chanIdx] -= *indataLen; 493 ilen = *indataLen; 494 foreach (immutable j; 0..N-1) x[j] = x[j+ilen]; 495 return Error.OK; 496 } 497 498 int magic (uint chanIdx, float **outdata, uint outdataLen) { 499 uint tempInLen = magicSamples.ptr[chanIdx]; 500 float* x = mem+chanIdx*memAllocSize; 501 processNative(chanIdx, &tempInLen, *outdata, &outdataLen); 502 magicSamples.ptr[chanIdx] -= tempInLen; 503 // if we couldn't process all "magic" input samples, save the rest for next time 504 if (magicSamples.ptr[chanIdx]) { 505 immutable N = filterLen; 506 foreach (immutable i; 0..magicSamples.ptr[chanIdx]) x[N-1+i] = x[N-1+i+tempInLen]; 507 } 508 *outdata += outdataLen*outStride; 509 return outdataLen; 510 } 511 512 Error updateFilter () { 513 uint oldFilterLen = filterLen; 514 uint oldAllocSize = memAllocSize; 515 bool useDirect; 516 uint minSincTableLen; 517 uint minAllocSize; 518 519 intAdvance = numRate/denRate; 520 fracAdvance = numRate%denRate; 521 oversample = qualityMap.ptr[srQuality].oversample; 522 filterLen = qualityMap.ptr[srQuality].baseLength; 523 524 if (numRate > denRate) { 525 // down-sampling 526 cutoff = qualityMap.ptr[srQuality].downsampleBandwidth*denRate/numRate; 527 // FIXME: divide the numerator and denominator by a certain amount if they're too large 528 filterLen = filterLen*numRate/denRate; 529 // round up to make sure we have a multiple of 8 for SSE 530 filterLen = ((filterLen-1)&(~0x7))+8; 531 if (2*denRate < numRate) oversample >>= 1; 532 if (4*denRate < numRate) oversample >>= 1; 533 if (8*denRate < numRate) oversample >>= 1; 534 if (16*denRate < numRate) oversample >>= 1; 535 if (oversample < 1) oversample = 1; 536 } else { 537 // up-sampling 538 cutoff = qualityMap.ptr[srQuality].upsampleBandwidth; 539 } 540 541 // choose the resampling type that requires the least amount of memory 542 version(sincresample_use_full_table) { 543 useDirect = true; 544 if (int.max/float.sizeof/denRate < filterLen) goto fail; 545 } else { 546 useDirect = (filterLen*denRate <= filterLen*oversample+8 && int.max/float.sizeof/denRate >= filterLen); 547 } 548 549 if (useDirect) { 550 minSincTableLen = filterLen*denRate; 551 } else { 552 if ((int.max/float.sizeof-8)/oversample < filterLen) goto fail; 553 minSincTableLen = filterLen*oversample+8; 554 } 555 556 if (sincTableLen < minSincTableLen) { 557 import core.stdc.stdlib : realloc; 558 auto nslen = cast(uint)(minSincTableLen*float.sizeof); 559 if (nslen > realSincTableLen) { 560 if (nslen < 512*1024) nslen = 512*1024; // inc to 3 mb? 561 auto x = cast(float*)realloc(sincTable, nslen); 562 if (!x) goto fail; 563 sincTable = x; 564 realSincTableLen = nslen; 565 } 566 sincTableLen = minSincTableLen; 567 } 568 569 if (useDirect) { 570 foreach (int i; 0..denRate) { 571 foreach (int j; 0..filterLen) { 572 sincTable[i*filterLen+j] = sinc(cutoff, ((j-cast(int)filterLen/2+1)-(cast(float)i)/denRate), filterLen, qualityMap.ptr[srQuality].windowFunc); 573 } 574 } 575 if (srQuality > 8) { 576 resampler = &resamplerBasicDirect!double; 577 } else { 578 resampler = &resamplerBasicDirect!float; 579 } 580 } else { 581 foreach (immutable int i; -4..cast(int)(oversample*filterLen+4)) { 582 sincTable[i+4] = sinc(cutoff, (i/cast(float)oversample-filterLen/2), filterLen, qualityMap.ptr[srQuality].windowFunc); 583 } 584 if (srQuality > 8) { 585 resampler = &resamplerBasicInterpolate!double; 586 } else { 587 resampler = &resamplerBasicInterpolate!float; 588 } 589 } 590 591 /* Here's the place where we update the filter memory to take into account 592 the change in filter length. It's probably the messiest part of the code 593 due to handling of lots of corner cases. */ 594 595 // adding bufferSize to filterLen won't overflow here because filterLen could be multiplied by float.sizeof above 596 minAllocSize = filterLen-1+bufferSize; 597 if (minAllocSize > memAllocSize) { 598 import core.stdc.stdlib : realloc; 599 if (int.max/float.sizeof/chanCount < minAllocSize) goto fail; 600 auto nslen = cast(uint)(chanCount*minAllocSize*mem[0].sizeof); 601 if (nslen > realMemLen) { 602 if (nslen < 16384) nslen = 16384; 603 auto x = cast(float*)realloc(mem, nslen); 604 if (x is null) goto fail; 605 mem = x; 606 realMemLen = nslen; 607 } 608 memAllocSize = minAllocSize; 609 } 610 if (!started) { 611 //foreach (i=0;i<chanCount*memAllocSize;i++) mem[i] = 0; 612 mem[0..chanCount*memAllocSize] = 0; 613 } else if (filterLen > oldFilterLen) { 614 // increase the filter length 615 foreach_reverse (uint i; 0..chanCount) { 616 uint j; 617 uint olen = oldFilterLen; 618 { 619 // try and remove the magic samples as if nothing had happened 620 //FIXME: this is wrong but for now we need it to avoid going over the array bounds 621 olen = oldFilterLen+2*magicSamples.ptr[i]; 622 for (j = oldFilterLen-1+magicSamples.ptr[i]; j--; ) mem[i*memAllocSize+j+magicSamples.ptr[i]] = mem[i*oldAllocSize+j]; 623 //for (j = 0; j < magicSamples.ptr[i]; ++j) mem[i*memAllocSize+j] = 0; 624 mem[i*memAllocSize..i*memAllocSize+magicSamples.ptr[i]] = 0; 625 magicSamples.ptr[i] = 0; 626 } 627 if (filterLen > olen) { 628 // if the new filter length is still bigger than the "augmented" length 629 // copy data going backward 630 for (j = 0; j < olen-1; ++j) mem[i*memAllocSize+(filterLen-2-j)] = mem[i*memAllocSize+(olen-2-j)]; 631 // then put zeros for lack of anything better 632 for (; j < filterLen-1; ++j) mem[i*memAllocSize+(filterLen-2-j)] = 0; 633 // adjust lastSample 634 lastSample.ptr[i] += (filterLen-olen)/2; 635 } else { 636 // put back some of the magic! 637 magicSamples.ptr[i] = (olen-filterLen)/2; 638 for (j = 0; j < filterLen-1+magicSamples.ptr[i]; ++j) mem[i*memAllocSize+j] = mem[i*memAllocSize+j+magicSamples.ptr[i]]; 639 } 640 } 641 } else if (filterLen < oldFilterLen) { 642 // reduce filter length, this a bit tricky 643 // we need to store some of the memory as "magic" samples so they can be used directly as input the next time(s) 644 foreach (immutable i; 0..chanCount) { 645 uint j; 646 uint oldMagic = magicSamples.ptr[i]; 647 magicSamples.ptr[i] = (oldFilterLen-filterLen)/2; 648 // we must copy some of the memory that's no longer used 649 // copy data going backward 650 for (j = 0; j < filterLen-1+magicSamples.ptr[i]+oldMagic; ++j) { 651 mem[i*memAllocSize+j] = mem[i*memAllocSize+j+magicSamples.ptr[i]]; 652 } 653 magicSamples.ptr[i] += oldMagic; 654 } 655 } 656 return Error.OK; 657 658 fail: 659 resampler = null; 660 /* mem may still contain consumed input samples for the filter. 661 Restore filterLen so that filterLen-1 still points to the position after 662 the last of these samples. */ 663 filterLen = oldFilterLen; 664 return Error.NoMemory; 665 } 666 } 667 668 enum BUFFER_SIZE_FRAMES = 1024;//512;//2048; 669 enum BUFFER_SIZE_SHORT = BUFFER_SIZE_FRAMES * 2; 670 671 672 // ////////////////////////////////////////////////////////////////////////// // 673 static immutable double[68] kaiser12Table = [ 674 0.99859849, 1.00000000, 0.99859849, 0.99440475, 0.98745105, 0.97779076, 675 0.96549770, 0.95066529, 0.93340547, 0.91384741, 0.89213598, 0.86843014, 676 0.84290116, 0.81573067, 0.78710866, 0.75723148, 0.72629970, 0.69451601, 677 0.66208321, 0.62920216, 0.59606986, 0.56287762, 0.52980938, 0.49704014, 678 0.46473455, 0.43304576, 0.40211431, 0.37206735, 0.34301800, 0.31506490, 679 0.28829195, 0.26276832, 0.23854851, 0.21567274, 0.19416736, 0.17404546, 680 0.15530766, 0.13794294, 0.12192957, 0.10723616, 0.09382272, 0.08164178, 681 0.07063950, 0.06075685, 0.05193064, 0.04409466, 0.03718069, 0.03111947, 682 0.02584161, 0.02127838, 0.01736250, 0.01402878, 0.01121463, 0.00886058, 683 0.00691064, 0.00531256, 0.00401805, 0.00298291, 0.00216702, 0.00153438, 684 0.00105297, 0.00069463, 0.00043489, 0.00025272, 0.00013031, 0.0000527734, 685 0.00001000, 0.00000000]; 686 687 static immutable double[36] kaiser10Table = [ 688 0.99537781, 1.00000000, 0.99537781, 0.98162644, 0.95908712, 0.92831446, 689 0.89005583, 0.84522401, 0.79486424, 0.74011713, 0.68217934, 0.62226347, 690 0.56155915, 0.50119680, 0.44221549, 0.38553619, 0.33194107, 0.28205962, 691 0.23636152, 0.19515633, 0.15859932, 0.12670280, 0.09935205, 0.07632451, 692 0.05731132, 0.04193980, 0.02979584, 0.02044510, 0.01345224, 0.00839739, 693 0.00488951, 0.00257636, 0.00115101, 0.00035515, 0.00000000, 0.00000000]; 694 695 static immutable double[36] kaiser8Table = [ 696 0.99635258, 1.00000000, 0.99635258, 0.98548012, 0.96759014, 0.94302200, 697 0.91223751, 0.87580811, 0.83439927, 0.78875245, 0.73966538, 0.68797126, 698 0.63451750, 0.58014482, 0.52566725, 0.47185369, 0.41941150, 0.36897272, 699 0.32108304, 0.27619388, 0.23465776, 0.19672670, 0.16255380, 0.13219758, 700 0.10562887, 0.08273982, 0.06335451, 0.04724088, 0.03412321, 0.02369490, 701 0.01563093, 0.00959968, 0.00527363, 0.00233883, 0.00050000, 0.00000000]; 702 703 static immutable double[36] kaiser6Table = [ 704 0.99733006, 1.00000000, 0.99733006, 0.98935595, 0.97618418, 0.95799003, 705 0.93501423, 0.90755855, 0.87598009, 0.84068475, 0.80211977, 0.76076565, 706 0.71712752, 0.67172623, 0.62508937, 0.57774224, 0.53019925, 0.48295561, 707 0.43647969, 0.39120616, 0.34752997, 0.30580127, 0.26632152, 0.22934058, 708 0.19505503, 0.16360756, 0.13508755, 0.10953262, 0.08693120, 0.06722600, 709 0.05031820, 0.03607231, 0.02432151, 0.01487334, 0.00752000, 0.00000000]; 710 711 struct FuncDef { 712 immutable(double)* table; 713 int oversample; 714 } 715 716 static immutable FuncDef Kaiser12 = FuncDef(kaiser12Table.ptr, 64); 717 static immutable FuncDef Kaiser10 = FuncDef(kaiser10Table.ptr, 32); 718 static immutable FuncDef Kaiser8 = FuncDef(kaiser8Table.ptr, 32); 719 static immutable FuncDef Kaiser6 = FuncDef(kaiser6Table.ptr, 32); 720 721 722 struct QualityMapping { 723 int baseLength; 724 int oversample; 725 float downsampleBandwidth; 726 float upsampleBandwidth; 727 immutable FuncDef* windowFunc; 728 } 729 730 731 /* This table maps conversion quality to internal parameters. There are two 732 reasons that explain why the up-sampling bandwidth is larger than the 733 down-sampling bandwidth: 734 1) When up-sampling, we can assume that the spectrum is already attenuated 735 close to the Nyquist rate (from an A/D or a previous resampling filter) 736 2) Any aliasing that occurs very close to the Nyquist rate will be masked 737 by the sinusoids/noise just below the Nyquist rate (guaranteed only for 738 up-sampling). 739 */ 740 static immutable QualityMapping[11] qualityMap = [ 741 QualityMapping( 8, 4, 0.830f, 0.860f, &Kaiser6 ), /* Q0 */ 742 QualityMapping( 16, 4, 0.850f, 0.880f, &Kaiser6 ), /* Q1 */ 743 QualityMapping( 32, 4, 0.882f, 0.910f, &Kaiser6 ), /* Q2 */ /* 82.3% cutoff ( ~60 dB stop) 6 */ 744 QualityMapping( 48, 8, 0.895f, 0.917f, &Kaiser8 ), /* Q3 */ /* 84.9% cutoff ( ~80 dB stop) 8 */ 745 QualityMapping( 64, 8, 0.921f, 0.940f, &Kaiser8 ), /* Q4 */ /* 88.7% cutoff ( ~80 dB stop) 8 */ 746 QualityMapping( 80, 16, 0.922f, 0.940f, &Kaiser10), /* Q5 */ /* 89.1% cutoff (~100 dB stop) 10 */ 747 QualityMapping( 96, 16, 0.940f, 0.945f, &Kaiser10), /* Q6 */ /* 91.5% cutoff (~100 dB stop) 10 */ 748 QualityMapping(128, 16, 0.950f, 0.950f, &Kaiser10), /* Q7 */ /* 93.1% cutoff (~100 dB stop) 10 */ 749 QualityMapping(160, 16, 0.960f, 0.960f, &Kaiser10), /* Q8 */ /* 94.5% cutoff (~100 dB stop) 10 */ 750 QualityMapping(192, 32, 0.968f, 0.968f, &Kaiser12), /* Q9 */ /* 95.5% cutoff (~100 dB stop) 10 */ 751 QualityMapping(256, 32, 0.975f, 0.975f, &Kaiser12), /* Q10 */ /* 96.6% cutoff (~100 dB stop) 10 */ 752 ]; 753 754 755 nothrow @trusted @nogc: 756 /*8, 24, 40, 56, 80, 104, 128, 160, 200, 256, 320*/ 757 double computeFunc (float x, immutable FuncDef* func) 758 { 759 version(Posix) import core.stdc.math : lrintf; 760 import std.math : floor; 761 //double[4] interp; 762 float y = x*func.oversample; 763 version(Posix) { 764 int ind = cast(int)lrintf(floor(y)); 765 } else { 766 int ind = cast(int)(floor(y)); 767 } 768 float frac = (y-ind); 769 immutable f2 = frac*frac; 770 immutable f3 = f2*frac; 771 double interp3 = -0.1666666667*frac+0.1666666667*(f3); 772 double interp2 = frac+0.5*(f2)-0.5*(f3); 773 //double interp2 = 1.0f-0.5f*frac-f2+0.5f*f3; 774 double interp0 = -0.3333333333*frac+0.5*(f2)-0.1666666667*(f3); 775 // just to make sure we don't have rounding problems 776 double interp1 = 1.0f-interp3-interp2-interp0; 777 //sum = frac*accum[1]+(1-frac)*accum[2]; 778 return interp0*func.table[ind]+interp1*func.table[ind+1]+interp2*func.table[ind+2]+interp3*func.table[ind+3]; 779 } 780 781 782 // the slow way of computing a sinc for the table; should improve that some day 783 float sinc (float cutoff, float x, int N, immutable FuncDef *windowFunc) 784 { 785 version(LittleEndian) { 786 align(1) union temp_float { align(1): float f; uint n; } 787 } else { 788 static T fabs(T) (T n) pure { static if (__VERSION__ > 2067) pragma(inline, true); return (n < 0 ? -n : n); } 789 } 790 import std.math : sin, PI; 791 version(LittleEndian) { 792 temp_float txx = void; 793 txx.f = x; 794 txx.n &= 0x7fff_ffff; // abs 795 if (txx.f < 1.0e-6f) return cutoff; 796 if (txx.f > 0.5f*N) return 0; 797 } else { 798 if (fabs(x) < 1.0e-6f) return cutoff; 799 if (fabs(x) > 0.5f*N) return 0; 800 } 801 //FIXME: can it really be any slower than this? 802 immutable float xx = x*cutoff; 803 immutable pixx = PI*xx; 804 version(LittleEndian) { 805 return cutoff*sin(pixx)/pixx*computeFunc(2.0*txx.f/N, windowFunc); 806 } else { 807 return cutoff*sin(pixx)/pixx*computeFunc(fabs(2.0*x/N), windowFunc); 808 } 809 } 810 811 812 void cubicCoef (in float frac, float* interp) { 813 immutable f2 = frac*frac; 814 immutable f3 = f2*frac; 815 // compute interpolation coefficients; i'm not sure whether this corresponds to cubic interpolation but I know it's MMSE-optimal on a sinc 816 interp[0] = -0.16667f*frac+0.16667f*f3; 817 interp[1] = frac+0.5f*f2-0.5f*f3; 818 //interp[2] = 1.0f-0.5f*frac-f2+0.5f*f3; 819 interp[3] = -0.33333f*frac+0.5f*f2-0.16667f*f3; 820 // just to make sure we don't have rounding problems 821 interp[2] = 1.0-interp[0]-interp[1]-interp[3]; 822 } 823 824 825 // ////////////////////////////////////////////////////////////////////////// // 826 int resamplerBasicDirect(T) (ref SpeexResampler st, uint chanIdx, const(float)* indata, uint* indataLen, float* outdata, uint* outdataLen) 827 if (is(T == float) || is(T == double)) 828 { 829 auto N = st.filterLen; 830 static if (is(T == double)) assert(N%4 == 0); 831 int outSample = 0; 832 int lastSample = st.lastSample.ptr[chanIdx]; 833 uint sampFracNum = st.sampFracNum.ptr[chanIdx]; 834 const(float)* sincTable = st.sincTable; 835 immutable outStride = st.outStride; 836 immutable intAdvance = st.intAdvance; 837 immutable fracAdvance = st.fracAdvance; 838 immutable denRate = st.denRate; 839 T sum = void; 840 while (!(lastSample >= cast(int)(*indataLen) || outSample >= cast(int)(*outdataLen))) { 841 const(float)* sinct = &sincTable[sampFracNum*N]; 842 const(float)* iptr = &indata[lastSample]; 843 static if (is(T == float)) { 844 // at least 2x speedup with SSE here (but for unrolled loop) 845 if (N%4 == 0) { 846 version(sincresample_use_sse) { 847 //align(64) __gshared float[4] zero = 0; 848 align(64) __gshared float[4+128] zeroesBuf = 0; // dmd cannot into such aligns, alas 849 __gshared uint zeroesptr = 0; 850 if (zeroesptr == 0) { 851 zeroesptr = cast(uint)zeroesBuf.ptr; 852 if (zeroesptr&0x3f) zeroesptr = (zeroesptr|0x3f)+1; 853 } 854 //assert((zeroesptr&0x3f) == 0, "wtf?!"); 855 asm nothrow @safe @nogc { 856 mov ECX,[N]; 857 shr ECX,2; 858 mov EAX,[zeroesptr]; 859 movaps XMM0,[EAX]; 860 mov EAX,[sinct]; 861 mov EBX,[iptr]; 862 mov EDX,16; 863 align 8; 864 rbdseeloop: 865 movups XMM1,[EAX]; 866 movups XMM2,[EBX]; 867 mulps XMM1,XMM2; 868 addps XMM0,XMM1; 869 add EAX,EDX; 870 add EBX,EDX; 871 dec ECX; 872 jnz rbdseeloop; 873 // store result in sum 874 movhlps XMM1,XMM0; // now low part of XMM1 contains high part of XMM0 875 addps XMM0,XMM1; // low part of XMM0 is ok 876 movaps XMM1,XMM0; 877 shufps XMM1,XMM0,0b_01_01_01_01; // 2nd float of XMM0 goes to the 1st float of XMM1 878 addss XMM0,XMM1; 879 movss [sum],XMM0; 880 } 881 /* 882 float sum1 = 0; 883 foreach (immutable j; 0..N) sum1 += sinct[j]*iptr[j]; 884 import std.math; 885 if (fabs(sum-sum1) > 0.000001f) { 886 import core.stdc.stdio; 887 printf("sum=%f; sum1=%f\n", sum, sum1); 888 assert(0); 889 } 890 */ 891 } else { 892 // no SSE; for my i3 unrolled loop is almost of the speed of SSE code 893 T[4] accum = 0; 894 foreach (immutable j; 0..N/4) { 895 accum.ptr[0] += *sinct++ * *iptr++; 896 accum.ptr[1] += *sinct++ * *iptr++; 897 accum.ptr[2] += *sinct++ * *iptr++; 898 accum.ptr[3] += *sinct++ * *iptr++; 899 } 900 sum = accum.ptr[0]+accum.ptr[1]+accum.ptr[2]+accum.ptr[3]; 901 } 902 } else { 903 sum = 0; 904 foreach (immutable j; 0..N) sum += *sinct++ * *iptr++; 905 } 906 outdata[outStride*outSample++] = sum; 907 } else { 908 if (N%4 == 0) { 909 //TODO: write SSE code here! 910 // for my i3 unrolled loop is ~2 times faster 911 T[4] accum = 0; 912 foreach (immutable j; 0..N/4) { 913 accum.ptr[0] += cast(double)*sinct++ * cast(double)*iptr++; 914 accum.ptr[1] += cast(double)*sinct++ * cast(double)*iptr++; 915 accum.ptr[2] += cast(double)*sinct++ * cast(double)*iptr++; 916 accum.ptr[3] += cast(double)*sinct++ * cast(double)*iptr++; 917 } 918 sum = accum.ptr[0]+accum.ptr[1]+accum.ptr[2]+accum.ptr[3]; 919 } else { 920 sum = 0; 921 foreach (immutable j; 0..N) sum += cast(double)*sinct++ * cast(double)*iptr++; 922 } 923 outdata[outStride*outSample++] = cast(float)sum; 924 } 925 lastSample += intAdvance; 926 sampFracNum += fracAdvance; 927 if (sampFracNum >= denRate) { 928 sampFracNum -= denRate; 929 ++lastSample; 930 } 931 } 932 st.lastSample.ptr[chanIdx] = lastSample; 933 st.sampFracNum.ptr[chanIdx] = sampFracNum; 934 return outSample; 935 } 936 937 938 int resamplerBasicInterpolate(T) (ref SpeexResampler st, uint chanIdx, const(float)* indata, uint *indataLen, float *outdata, uint *outdataLen) 939 if (is(T == float) || is(T == double)) 940 { 941 immutable N = st.filterLen; 942 assert(N%4 == 0); 943 int outSample = 0; 944 int lastSample = st.lastSample.ptr[chanIdx]; 945 uint sampFracNum = st.sampFracNum.ptr[chanIdx]; 946 immutable outStride = st.outStride; 947 immutable intAdvance = st.intAdvance; 948 immutable fracAdvance = st.fracAdvance; 949 immutable denRate = st.denRate; 950 float sum; 951 952 float[4] interp = void; 953 T[4] accum = void; 954 while (!(lastSample >= cast(int)(*indataLen) || outSample >= cast(int)(*outdataLen))) { 955 const(float)* iptr = &indata[lastSample]; 956 const int offset = sampFracNum*st.oversample/st.denRate; 957 const float frac = (cast(float)((sampFracNum*st.oversample)%st.denRate))/st.denRate; 958 accum[] = 0; 959 //TODO: optimize! 960 foreach (immutable j; 0..N) { 961 immutable T currIn = iptr[j]; 962 accum.ptr[0] += currIn*(st.sincTable[4+(j+1)*st.oversample-offset-2]); 963 accum.ptr[1] += currIn*(st.sincTable[4+(j+1)*st.oversample-offset-1]); 964 accum.ptr[2] += currIn*(st.sincTable[4+(j+1)*st.oversample-offset]); 965 accum.ptr[3] += currIn*(st.sincTable[4+(j+1)*st.oversample-offset+1]); 966 } 967 968 cubicCoef(frac, interp.ptr); 969 sum = (interp.ptr[0]*accum.ptr[0])+(interp.ptr[1]*accum.ptr[1])+(interp.ptr[2]*accum.ptr[2])+(interp.ptr[3]*accum.ptr[3]); 970 971 outdata[outStride*outSample++] = sum; 972 lastSample += intAdvance; 973 sampFracNum += fracAdvance; 974 if (sampFracNum >= denRate) { 975 sampFracNum -= denRate; 976 ++lastSample; 977 } 978 } 979 980 st.lastSample.ptr[chanIdx] = lastSample; 981 st.sampFracNum.ptr[chanIdx] = sampFracNum; 982 return outSample; 983 } 984 985 986 // ////////////////////////////////////////////////////////////////////////// // 987 uint gcd (uint a, uint b) pure { 988 if (a == 0) return b; 989 if (b == 0) return a; 990 for (;;) { 991 if (a > b) { 992 a %= b; 993 if (a == 0) return b; 994 if (a == 1) return 1; 995 } else { 996 b %= a; 997 if (b == 0) return a; 998 if (b == 1) return 1; 999 } 1000 } 1001 } 1002 1003 1004 // ////////////////////////////////////////////////////////////////////////// // 1005 // very simple and cheap cubic upsampler 1006 struct CubicUpsampler 1007 { 1008 public: 1009 nothrow @trusted @nogc: 1010 float[2] curposfrac; // current position offset [0..1) 1011 float step; // how long we should move on one step? 1012 float[4][2] data; // -1..3 1013 uint[2] drain; 1014 1015 void reset () 1016 { 1017 curposfrac[] = 0.0f; 1018 foreach (ref d; data) d[] = 0.0f; 1019 drain[] = 0; 1020 } 1021 1022 bool setup (float astep) 1023 { 1024 if (astep >= 1.0f) return false; 1025 step = astep; 1026 return true; 1027 } 1028 1029 /* 1030 static struct Data { 1031 const(float)[] dataIn; 1032 float[] dataOut; 1033 uint inputSamplesUsed; // out value, in samples (i.e. multiplied by channel count) 1034 uint outputSamplesUsed; // out value, in samples (i.e. multiplied by channel count) 1035 } 1036 */ 1037 1038 SpeexResampler.Error process (ref SpeexResampler.Data d) 1039 { 1040 d.inputSamplesUsed = d.outputSamplesUsed = 0; 1041 if (d.dataOut.length < 2) return SpeexResampler.Error.OK; 1042 foreach (uint cidx; 0..2) { 1043 uint inleft = cast(uint)d.dataIn.length/2; 1044 uint outleft = cast(uint)d.dataOut.length/2; 1045 processChannel(inleft, outleft, (d.dataIn.length ? d.dataIn.ptr+cidx : null), (d.dataOut.length ? d.dataOut.ptr+cidx : null), cidx); 1046 d.outputSamplesUsed += cast(uint)(d.dataOut.length/2)-outleft; 1047 d.inputSamplesUsed += cast(uint)(d.dataIn.length/2)-inleft; 1048 } 1049 return SpeexResampler.Error.OK; 1050 } 1051 1052 private void processChannel (ref uint inleft, ref uint outleft, const(float)* dataIn, float* dataOut, uint cidx) 1053 { 1054 if (outleft == 0) return; 1055 if (inleft == 0 && drain.ptr[cidx] <= 1) return; 1056 auto dt = data.ptr[cidx].ptr; 1057 auto drn = drain.ptr+cidx; 1058 auto cpf = curposfrac.ptr+cidx; 1059 immutable float st = step; 1060 for (;;) { 1061 // fill buffer 1062 while ((*drn) < 4) { 1063 if (inleft == 0) return; 1064 dt[(*drn)++] = *dataIn; 1065 dataIn += 2; 1066 --inleft; 1067 } 1068 if (outleft == 0) return; 1069 --outleft; 1070 // cubic interpolation 1071 /*version(none)*/ { 1072 // interpolate between y1 and y2 1073 immutable float mu = (*cpf); // how far we are moved from y1 to y2 1074 immutable float mu2 = mu*mu; // wow 1075 immutable float y0 = dt[0], y1 = dt[1], y2 = dt[2], y3 = dt[3]; 1076 version(complex_cubic) { 1077 immutable float z0 = 0.5*y3; 1078 immutable float z1 = 0.5*y0; 1079 immutable float a0 = 1.5*y1-z1-1.5*y2+z0; 1080 immutable float a1 = y0-2.5*y1+2*y2-z0; 1081 immutable float a2 = 0.5*y2-z1; 1082 } else { 1083 immutable float a0 = y3-y2-y0+y1; 1084 immutable float a1 = y0-y1-a0; 1085 immutable float a2 = y2-y0; 1086 } 1087 *dataOut = a0*mu*mu2+a1*mu2+a2*mu+y1; 1088 }// else *dataOut = dt[1]; 1089 dataOut += 2; 1090 if (((*cpf) += st) >= 1.0f) 1091 { 1092 (*cpf) -= 1.0f; 1093 dt[0] = dt[1]; 1094 dt[1] = dt[2]; 1095 dt[2] = dt[3]; 1096 dt[3] = 0.0f; 1097 --(*drn); // will request more input bytes 1098 } 1099 } 1100 } 1101 } 1102 1103 1104 interface SampleController { 1105 /++ 1106 Pauses playback, keeping its position. Use [resume] to pick up where it left off. 1107 +/ 1108 void pause(); 1109 /++ 1110 Resumes playback after a call to [pause]. 1111 +/ 1112 void resume(); 1113 /++ 1114 Stops playback. Once stopped, it cannot be restarted 1115 except by creating a new sample from the [AudioOutputThread] 1116 object. 1117 +/ 1118 void stop(); 1119 /++ 1120 Reports the current stream position, in seconds, if available (NaN if not). 1121 +/ 1122 float position(); 1123 1124 /++ 1125 If the sample has finished playing. Happens when it runs out or if it is stopped. 1126 +/ 1127 bool finished(); 1128 1129 /++ 1130 If the sample has been paused. 1131 History: 1132 Added May 26, 2021 (dub v10.0) 1133 +/ 1134 bool paused(); 1135 } 1136 1137 package class SampleControlFlags : SampleController 1138 { 1139 void pause() { paused_ = true; } 1140 void resume() { paused_ = false; } 1141 void stop() { paused_ = false; stopped = true; } 1142 1143 bool paused_; 1144 bool stopped; 1145 bool finished_; 1146 1147 float position() { return currentPosition; } 1148 bool finished() { return finished_; } 1149 bool paused() { return paused_; } 1150 1151 float currentPosition = 0.0; 1152 } 1153 1154 1155 abstract class ResamplingContext { 1156 int inputSampleRate; 1157 int outputSampleRate; 1158 1159 int inputChannels; 1160 int outputChannels; 1161 1162 SpeexResampler resamplerLeft; 1163 SpeexResampler resamplerRight; 1164 1165 SpeexResampler.Data resamplerDataLeft; 1166 SpeexResampler.Data resamplerDataRight; 1167 1168 float[][2] buffersIn; 1169 float[][2] buffersOut; 1170 1171 uint rateNum; 1172 uint rateDem; 1173 1174 float[][2] dataReady; 1175 1176 SampleControlFlags scflags; 1177 1178 this(SampleControlFlags scflags, int inputSampleRate, int outputSampleRate, int inputChannels, int outputChannels) { 1179 this.scflags = scflags; 1180 this.inputSampleRate = inputSampleRate; 1181 this.outputSampleRate = outputSampleRate; 1182 this.inputChannels = inputChannels; 1183 this.outputChannels = outputChannels; 1184 1185 1186 if(auto err = resamplerLeft.setup(1, inputSampleRate, outputSampleRate, 5)) 1187 throw new Exception("ugh"); 1188 resamplerRight.setup(1, inputSampleRate, outputSampleRate, 5); 1189 1190 resamplerLeft.getRatio(rateNum, rateDem); 1191 1192 int add = (rateNum % rateDem) ? 1 : 0; 1193 1194 buffersIn[0] = new float[](BUFFER_SIZE_FRAMES * rateNum / rateDem + add); 1195 buffersOut[0] = new float[](BUFFER_SIZE_FRAMES); 1196 if(inputChannels > 1) { 1197 buffersIn[1] = new float[](BUFFER_SIZE_FRAMES * rateNum / rateDem + add); 1198 buffersOut[1] = new float[](BUFFER_SIZE_FRAMES); 1199 } 1200 } 1201 1202 /+ 1203 float*[2] tmp; 1204 tmp[0] = buffersIn[0].ptr; 1205 tmp[1] = buffersIn[1].ptr; 1206 auto actuallyGot = v.getSamplesFloat(v.chans, tmp.ptr, cast(int) buffersIn[0].length); 1207 resamplerDataLeft.dataIn should be a slice of buffersIn[0] that is filled up 1208 ditto for resamplerDataRight if the source has two channels 1209 +/ 1210 abstract void loadMoreSamples(); 1211 1212 bool loadMore() { 1213 resamplerDataLeft.dataIn = buffersIn[0]; 1214 resamplerDataLeft.dataOut = buffersOut[0]; 1215 1216 resamplerDataRight.dataIn = buffersIn[1]; 1217 resamplerDataRight.dataOut = buffersOut[1]; 1218 1219 loadMoreSamples(); 1220 1221 //resamplerLeft.reset(); 1222 1223 if(auto err = resamplerLeft.process(resamplerDataLeft)) 1224 throw new Exception("ugh"); 1225 if(inputChannels > 1) 1226 //resamplerRight.reset(); 1227 resamplerRight.process(resamplerDataRight); 1228 1229 resamplerDataLeft.dataOut = resamplerDataLeft.dataOut[0 .. resamplerDataLeft.outputSamplesUsed]; 1230 resamplerDataRight.dataOut = resamplerDataRight.dataOut[0 .. resamplerDataRight.outputSamplesUsed]; 1231 1232 if(resamplerDataLeft.dataOut.length == 0) { 1233 return true; 1234 } 1235 return false; 1236 } 1237 1238 1239 bool fillBuffer(short[] buffer) { 1240 if(cast(int) buffer.length != buffer.length) 1241 throw new Exception("eeeek"); 1242 1243 if(scflags.paused) { 1244 buffer[] = 0; 1245 return true; 1246 } 1247 1248 if(outputChannels == 1) { 1249 foreach(ref s; buffer) { 1250 if(resamplerDataLeft.dataOut.length == 0) { 1251 if(loadMore()) { 1252 scflags.finished_ = true; 1253 return false; 1254 } 1255 } 1256 1257 if(inputChannels == 1) { 1258 s = cast(short) (resamplerDataLeft.dataOut[0] * short.max); 1259 resamplerDataLeft.dataOut = resamplerDataLeft.dataOut[1 .. $]; 1260 } else { 1261 s = cast(short) ((resamplerDataLeft.dataOut[0] + resamplerDataRight.dataOut[0]) * short.max / 2); 1262 1263 resamplerDataLeft.dataOut = resamplerDataLeft.dataOut[1 .. $]; 1264 resamplerDataRight.dataOut = resamplerDataRight.dataOut[1 .. $]; 1265 } 1266 } 1267 1268 scflags.currentPosition += cast(float) buffer.length / outputSampleRate / outputChannels; 1269 } else if(outputChannels == 2) { 1270 foreach(idx, ref s; buffer) { 1271 if(resamplerDataLeft.dataOut.length == 0) { 1272 if(loadMore()) { 1273 scflags.finished_ = true; 1274 return false; 1275 } 1276 } 1277 1278 if(inputChannels == 1) { 1279 s = cast(short) (resamplerDataLeft.dataOut[0] * short.max); 1280 if(idx & 1) 1281 resamplerDataLeft.dataOut = resamplerDataLeft.dataOut[1 .. $]; 1282 } else { 1283 if(idx & 1) { 1284 s = cast(short) (resamplerDataRight.dataOut[0] * short.max); 1285 resamplerDataRight.dataOut = resamplerDataRight.dataOut[1 .. $]; 1286 } else { 1287 s = cast(short) (resamplerDataLeft.dataOut[0] * short.max); 1288 resamplerDataLeft.dataOut = resamplerDataLeft.dataOut[1 .. $]; 1289 } 1290 } 1291 } 1292 1293 scflags.currentPosition += cast(float) buffer.length / outputSampleRate / outputChannels; 1294 } else assert(0); 1295 1296 if(scflags.stopped) 1297 scflags.finished_ = true; 1298 return !scflags.stopped; 1299 } 1300 1301 bool fillBuffer(float[] buffer) { 1302 if(cast(int) buffer.length != buffer.length) 1303 throw new Exception("eeeek"); 1304 1305 if(scflags.paused) { 1306 buffer[] = 0; 1307 return true; 1308 } 1309 1310 if(outputChannels == 1) 1311 { 1312 foreach(ref s; buffer) 1313 { 1314 if(resamplerDataLeft.dataOut.length == 0) 1315 { 1316 if(loadMore()) 1317 { 1318 scflags.finished_ = true; 1319 return false; 1320 } 1321 } 1322 1323 if(inputChannels == 1) 1324 { 1325 s = resamplerDataLeft.dataOut[0]; 1326 resamplerDataLeft.dataOut = resamplerDataLeft.dataOut[1 .. $]; 1327 } 1328 else 1329 { 1330 s = (resamplerDataLeft.dataOut[0] + resamplerDataRight.dataOut[0]) / 2; 1331 1332 resamplerDataLeft.dataOut = resamplerDataLeft.dataOut[1 .. $]; 1333 resamplerDataRight.dataOut = resamplerDataRight.dataOut[1 .. $]; 1334 } 1335 } 1336 1337 scflags.currentPosition += cast(float) buffer.length / outputSampleRate / outputChannels; 1338 } 1339 else if(outputChannels == 2) 1340 { 1341 foreach(idx, ref s; buffer) 1342 { 1343 if(resamplerDataLeft.dataOut.length == 0) 1344 { 1345 if(loadMore()) 1346 { 1347 scflags.finished_ = true; 1348 return false; 1349 } 1350 } 1351 1352 if(inputChannels == 1) 1353 { 1354 s = resamplerDataLeft.dataOut[0]; 1355 if(idx & 1) 1356 resamplerDataLeft.dataOut = resamplerDataLeft.dataOut[1 .. $]; 1357 } 1358 else 1359 { 1360 if(idx & 1) 1361 { 1362 s = resamplerDataRight.dataOut[0]; 1363 resamplerDataRight.dataOut = resamplerDataRight.dataOut[1 .. $]; 1364 } 1365 else 1366 { 1367 s = resamplerDataLeft.dataOut[0]; 1368 resamplerDataLeft.dataOut = resamplerDataLeft.dataOut[1 .. $]; 1369 } 1370 } 1371 } 1372 1373 scflags.currentPosition += cast(float) buffer.length / outputSampleRate / outputChannels; 1374 } else assert(0); 1375 1376 if(scflags.stopped) 1377 scflags.finished_ = true; 1378 return !scflags.stopped; 1379 } 1380 }